<?php

/* SVN FILE: $Id$ */
/**
 * SassFile class file.
 * File handling utilites.
 * @author      Chris Yates <chris.l.yates@gmail.com>
 * @copyright   Copyright (c) 2010 PBM Web Development
 * @license      http://phamlp.googlecode.com/files/license.txt
 * @package      PHamlP
 * @subpackage  Sass
 */

/**
 * SassFile class.
 * @package      PHamlP
 * @subpackage  Sass
 */
class SassFile
{

    const CSS = 'css';
    const SASS = 'sass';
    const SCSS = 'scss';

    // const SASSC = 'sassc'; # tests for E_NOTICE

    private static $extensions = array(self::SASS, self::SCSS);
    public static $path = FALSE;
    public static $parser = FALSE;

    /**
     * Returns the parse tree for a file.
     * @param string filename to parse
     * @param SassParser Sass parser
     * @return SassRootNode
     */
    public static function get_tree($filename, &$parser)
    {
        $contents = self::get_file_contents($filename, $parser);

        $options = array_merge($parser->options, array('line' => 1));

        # attempt at cross-syntax imports.
        $ext = substr($filename, strrpos($filename, '.') + 1);
        if ($ext == self::SASS || $ext == self::SCSS) {
            $options['syntax'] = $ext;
        }

        $dirname = dirname($filename);
        $options['load_paths'][] = $dirname;
        if (!in_array($dirname, $parser->load_paths)) {
            $parser->load_paths[] = dirname($filename);
        }

        $sassParser = new SassParser($options);
        $tree = $sassParser->parse($contents, FALSE);
        return $tree;
    }

    public static function get_file_contents($filename, $parser)
    {
        $contents = file_get_contents($filename) . "\n\n "; #add some whitespace to fix bug
        # strip // comments at this stage, with allowances for http:// style locations.
        $contents = preg_replace("/(^|\s)\/\/[^\n]+/", '', $contents);
        // SassFile::$parser = $parser;
        // SassFile::$path = $filename;
        // $contents = preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', 'SassFile::resolve_paths', $contents);
        return $contents;
    }

    public static function resolve_paths($matches)
    {
        // Resolve the path into something nicer...
        return 'url("' . self::resolve_path($matches[1]) . '")';
    }

    public static function resolve_path($name)
    {
        $path = self::$parser->basepath . self::$path;
        $path = substr($path, 0, strrpos($path, '/')) . '/';
        $path = $path . $name;
        $last = '';
        while ($path != $last) {
            $last = $path;
            $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
        }
        return $path;
    }

    /**
     * Returns the full path to a file to parse.
     * The file is looked for recursively under the load_paths directories
     * If the filename does not end in .sass or .scss try the current syntax first
     * then, if a file is not found, try the other syntax.
     * @param string filename to find
     * @param SassParser Sass parser
     * @return array of string path(s) to file(s) or FALSE if no such file
     */
    public static function get_file($filename, &$parser, $sass_only = TRUE)
    {
        $ext = substr($filename, strrpos($filename, '.') + 1);
        // if the last char isn't *, and it's not (.sass|.scss|.css)
        if ($sass_only && substr($filename, -1) != '*' && $ext !== self::SASS && $ext !== self::SCSS && $ext !== self::CSS) {
            $sass = self::get_file($filename . '.' . self::SASS, $parser);
            return $sass ? $sass : self::get_file($filename . '.' . self::SCSS, $parser);
        }
        if (file_exists($filename)) {
            return array($filename);
        }
        $paths = $parser->load_paths;
        if (is_string($parser->filename) && $path = dirname($parser->filename)) {
            $paths[] = $path;
            if (!in_array($path, $parser->load_paths)) {
                $parser->load_paths[] = $path;
            }
        }
        foreach ($paths as $path) {
            $filepath = self::find_file($filename, realpath($path));
            if ($filepath !== false) {
                return array($filepath);
            }
        }
        foreach ($parser->load_path_functions as $function) {
            if (is_callable($function) && $paths = call_user_func($function, $filename, $parser)) {
                return $paths;
            }
        }
        return FALSE;
    }

    /**
     * Looks for the file recursively in the specified directory.
     * This will also look for _filename to handle Sass partials.
     * @param string filename to look for
     * @param string path to directory to look in and under
     * @return mixed string: full path to file if found, false if not
     */
    public static function find_file($filename, $dir)
    {
        $partialname = dirname($filename) . DIRECTORY_SEPARATOR . '_' . basename($filename);

        foreach (array($filename, $partialname) as $file) {
            if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) {
                return realpath($dir . DIRECTORY_SEPARATOR . $file);
            }
        }

        if (is_dir($dir)) {
            $files = array_slice(scandir($dir), 2);

            foreach ($files as $file) {
                if (is_dir($dir . DIRECTORY_SEPARATOR . $file)) {
                    $path = self::find_file($filename, $dir . DIRECTORY_SEPARATOR . $file);
                    if ($path !== false) {
                        return $path;
                    }
                }
            }
        }
        return false;
    }

}
